package util

import (
	"github.com/spf13/cast"
	"reflect"
	"strings"
)

// CompareModelAndMap is used to compare whether the field values of the model and map attributes are the same.
// The original field value set in the dataOri and the modified field value set in the data are returned
// Model is the original data object found in the database
// ParamMap is the map that is passed in after modification
func CompareModelAndMap(model interface{}, paramMap map[string]interface{}) (dataOri map[string]interface{}, data map[string]interface{}) {
	var modelValue reflect.Value
	kind := reflect.TypeOf(model).Kind()
	if kind == reflect.Struct {
		modelValue = reflect.ValueOf(model)
	} else if kind == reflect.Ptr {
		modelValue = reflect.ValueOf(model).Elem()
	} else {
		return
	}

	dataOri = make(map[string]interface{})
	data = make(map[string]interface{})
	//遍历map集合
	for key, mapValue := range paramMap {
		//获取字段类型和值
		fileName := MapKeyToFieldName(key)
		fieldValue := modelValue.FieldByName(fileName).Interface()
		if cast.ToString(fieldValue) != cast.ToString(mapValue) {
			dataOri[key] = fieldValue
			data[key] = mapValue
		}
	}
	return
}

// MapKeyToFieldName is used to convert the key of the map collection into the field property name of a model object
func MapKeyToFieldName(key string) string {
	// 将下划线分隔的字符串转换为驼峰命名
	parts := strings.Split(key, "_")
	for i := range parts {
		parts[i] = strings.Title(parts[i])
	}
	return strings.Join(parts, "")
}

// CompareModelAndMap is used to Compare the field property values of two objects
// return the modified fields and values VS the original fields and values
// oldObj is the original data object found in the database
// newObj is a modified object
func CompareTwoObject(oldObj, newObj interface{}) (oldMap, newMap map[string]interface{}) {

	oldMap = make(map[string]interface{})
	newMap = make(map[string]interface{})

	oldVal := reflect.ValueOf(oldObj).Elem()
	newVal := reflect.ValueOf(newObj).Elem()

	if oldVal.Kind() != reflect.Struct || newVal.Kind() != reflect.Struct {
		return
	}

	oldType := oldVal.Type()
	for i := 0; i < oldVal.NumField(); i++ {
		fieldName := oldType.Field(i).Name
		oldValue := oldVal.Field(i).Interface()
		newValue := newVal.Field(i).Interface()

		if oldVal.Field(i).Kind() == reflect.Struct && newVal.Field(i).Kind() == reflect.Struct {
			subOldVal := oldVal.Field(i)
			subNewVal := newVal.Field(i)
			subOldType := subOldVal.Type()
			for j := 0; j < subOldType.NumField(); j++ {
				subFieldName := subOldType.Field(j).Name
				subOldValue := subOldVal.Field(j).Interface()
				subNewValue := subNewVal.Field(j).Interface()
				if !reflect.DeepEqual(subOldValue, subNewValue) {
					oldMap[subFieldName] = subOldValue
					newMap[subFieldName] = subNewValue
				}
			}
		} else {
			if !reflect.DeepEqual(oldValue, newValue) {
				oldMap[fieldName] = oldValue
				newMap[fieldName] = newValue
			}
		}
	}
	return
}

// CompareModelAndMap is used to compare two map sets to obtain field information with the same difference and field name but different values
// The original field value set in the dataOri and the modified field value set in the data are returned
func CompareTwoMap(originMap, modifyMap map[string]interface{}) (dataOri map[string]interface{}, data map[string]interface{}) {
	dataOri = make(map[string]interface{})
	data = make(map[string]interface{})
	//遍历modifyMap集合
	for key, modifyValue := range modifyMap {
		if originValue, ok := originMap[key]; ok {
			if cast.ToString(modifyValue) != cast.ToString(originValue) {
				dataOri[key] = originValue
				data[key] = modifyValue
			}
		} else {
			data[key] = modifyValue
		}
	}

	//遍历originMap集合
	for key, originValue := range originMap {
		if _, ok := modifyMap[key]; !ok {
			dataOri[key] = originValue
		}
	}
	return
}
